﻿Imports Framework.Data

Namespace TransferEngine
    Friend Class Manager
        Private Shared mDownloadDaemonThread As Threading.Thread

        Public Shared Sub Start()
            If mDownloadDaemonThread IsNot Nothing Then Throw New Exception("Already started!")

            DownloadQueue.Start()

            mDownloadDaemonThread = New Threading.Thread(AddressOf DownloadTransferDaemon)
            mDownloadDaemonThread.Name = "File Share Transfer Download Request Daemon Thread"
            mDownloadDaemonThread.Priority = Threading.ThreadPriority.BelowNormal
            mDownloadDaemonThread.IsBackground = True
            mDownloadDaemonThread.Start()

            Framework.Net.Manager.RegisterDataChannel(New FileShareDataChannel)
        End Sub

        Public Shared Sub [Stop](ByVal message As Framework.Message)
            DownloadQueue.Stop()

            If mDownloadDaemonThread IsNot Nothing Then
                message.SetMessage("Shutting down downloads...")
                mDownloadDaemonThread.Abort()
                mDownloadDaemonThread.Join(10000)
                mDownloadDaemonThread = Nothing
            End If
        End Sub

        Private Shared Sub DownloadTransferDaemon()
            'this daemon will handle downloads
            Try
                Dim blnPauseDownload As Boolean = False
                Dim dtLastSpaceCheck As Date = DateTime.MinValue
                Do
                    Try
                        Dim clsLastFile As Entities.Downloads.File
                        clsLastFile = DownloadQueue.HandleAnyFinishedDownloadedPart()

                        Dim clsNewFile As Entities.Downloads.File = Nothing
                        If Not blnPauseDownload Then
                            Dim clsReq As PartRequest = DownloadQueue.GetNextPartToDownload()
                            If clsReq IsNot Nothing Then
                                Dim iMessageId As Integer
                                iMessageId = Commands.RequestFilePart.SendRequest(clsReq.Node, clsReq.FileSHA512, clsReq.Size, clsReq.PartId, clsReq.TrackingId)
                                clsNewFile = DownloadQueue.AddAsActivePart(clsReq, iMessageId)
                            End If
                        End If

                        DownloadQueue.UpdateFileStatus(clsLastFile, clsNewFile)

                        If dtLastSpaceCheck < Now.AddSeconds(-60) Then
                            'time to check if disk space is low
                            Dim strPath As String = IO.Path.GetPathRoot(Settings.IncomingFilesDirectory)
                            Dim lBytesFree As Long = Framework.Win32.GetFreeDiskSpace(strPath)
                            If Settings.PauseDownloadingBelowVolumeBytesFree > lBytesFree Then
                                blnPauseDownload = True
                            Else
                                blnPauseDownload = False
                            End If
                            If Settings.RecycleBelowVolumeBytesFree > lBytesFree Then
                                RecycleDownloads(Settings.RecycleBelowVolumeBytesFree - lBytesFree)
                            End If
                            dtLastSpaceCheck = Now
                        End If

                        If blnPauseDownload Then
                            Threading.Thread.Sleep(2500)
                        Else
                            Threading.Thread.Sleep(100)
                        End If

                    Catch ex As Threading.ThreadAbortException
                        Exit Do
                    Catch ex As Exception
                        Framework.Misc.HandleException(ex)
                    End Try
                Loop
            Catch ex As Threading.ThreadAbortException
                Return
            Catch ex As Exception
                Framework.Misc.HandleException(ex)
            End Try
        End Sub

        Private Shared Sub RecycleDownloads(ByVal bytesToRecycle As Long)
            Dim aFiles As Generic.List(Of Entities.Downloads.File)
            aFiles = EntityHandlers.DownloadsFiles.GetAllByStatus(Entities.Downloads.Status.Recycled)
            If aFiles.Count > 0 Then
                Dim aSorted As New Generic.SortedList(Of String, Entities.Downloads.File)
                For Each clsFile As Entities.Downloads.File In aFiles
                    aSorted.Add(clsFile.Completed.ToString("yyyyMMddHHmmss"), clsFile)
                Next
                While aSorted.Count > 0 AndAlso bytesToRecycle > 0
                    Dim clsFile As Entities.Downloads.File = aSorted.Values(0)
                    Dim strFilePath As String = IO.Path.Combine(clsFile.Directory, clsFile.Name)
                    IO.File.Delete(strFilePath)
                    EntityHandlers.DownloadsFiles.Delete(clsFile.Id)
                    bytesToRecycle -= clsFile.Size
                    aSorted.RemoveAt(0)
                    FileShare.Engine.Manager.OnLogEvent(String.Format("Deleted recycled file {0} to save disk space", strFilePath))
                End While
            End If
        End Sub


        Friend Shared Sub QueueUploadPartRequest(ByVal partToSend As TransmitPartWorkItem)
            FileShareDataChannel.QueueWorkItem(partToSend)
        End Sub
        Friend Shared Sub CancelUploadPartRequest(ByVal node As Framework.Net.Node, ByVal trackingId As String)
            FileShareDataChannel.CancelUploadPartRequest(node, trackingId)
        End Sub


        Public Shared Sub StartDownload(ByVal sharedItem As Commands.GetShareItems.ItemInfo, ByVal sharedFiles As Generic.List(Of Entities.SharedItemFile))
            Dim clsItem As Entities.Downloads.Item

            clsItem = EntityHandlers.DownloadsItems.GetByUniqueId(sharedItem.UniqueId)
            If clsItem Is Nothing Then
                clsItem = New Entities.Downloads.Item
                clsItem.ItemType = sharedItem.Type
                clsItem.Name = sharedItem.Name
                clsItem.Priority = Entities.Priority.Normal
                clsItem.UniqueId = sharedItem.UniqueId
            End If

            Dim strDownloadDirectory As String = IO.Path.Combine(Settings.DownloadingFilesDirectory, clsItem.Name)
            If Not IO.Directory.Exists(strDownloadDirectory) Then IO.Directory.CreateDirectory(strDownloadDirectory)

            Dim aFiles As New Generic.List(Of Entities.Downloads.File)
            For Each clsSharedFile As Entities.SharedItemFile In sharedFiles
                Dim clsFile As New Entities.Downloads.File
                'now, see if we have this file in "downloading"
                clsFile = EntityHandlers.DownloadsFiles.GetBySHA512HashAndSize(clsSharedFile.SHA512Hash, clsSharedFile.Size)
                If clsFile Is Nothing Then
                    clsFile = New Entities.Downloads.File
                End If
                clsFile.Status = Entities.Downloads.Status.Waiting
                clsFile.Directory = strDownloadDirectory
                clsFile.Name = clsSharedFile.FileName
                clsFile.PartsCompleted = 0
                clsFile.PartCount = CalculatePartCountEx(clsSharedFile.Size)
                clsFile.PartStatuses = CreatePartStatusArray(clsSharedFile.Size, Entities.Downloads.PartStatus.Missing)
                If clsFile.Name.ToLower.EndsWith(".omx") Then
                    clsFile.Priority = Entities.Priority.High
                Else
                    clsFile.Priority = Entities.Priority.Normal
                End If
                clsFile.Size = clsSharedFile.Size
                clsFile.Started = Now
                clsFile.SHA512Hash = clsSharedFile.SHA512Hash
                aFiles.Add(clsFile)

                'now, create an empty file of the right size on disk
                Dim strPath As String = IO.Path.Combine(clsFile.Directory, clsFile.Name)
                Using fs As IO.FileStream = IO.File.Create(strPath)
                    fs.SetLength(clsFile.Size)
                End Using
            Next

            'now, start transaction and save the items and all its files 
            Using t As New TransactionScope
                EntityHandlers.DownloadsItems.Save(clsItem, t)
                For Each clsFile As Entities.Downloads.File In aFiles
                    clsFile.ItemId = clsItem.Id
                    EntityHandlers.DownloadsFiles.Save(clsFile, t)
                Next
                t.Complete()
            End Using

            'now, write metadata to alternate data stream (if filesystem supports it)
            For Each clsFile As Entities.Downloads.File In aFiles
                Handler_ADS.UpdateADSMetaData(clsItem, clsFile, False)
            Next

            DownloadQueue.AddNewItems(clsItem, aFiles)
        End Sub

        Public Shared Sub Recycle(ByVal file As Entities.Downloads.File)
            Dim aFiles As New Generic.List(Of Entities.Downloads.File)
            aFiles.Add(file)
            Recycle(aFiles)
        End Sub
        Public Shared Sub [Resume](ByVal files As Generic.List(Of Entities.Downloads.File))
            DownloadQueue.ResumeDownloading(files)
        End Sub
        Public Shared Sub Pause(ByVal files As Generic.List(Of Entities.Downloads.File))
            DownloadQueue.PauseDownloading(files)
        End Sub
        Public Shared Sub Delete(ByVal files As Generic.List(Of Entities.Downloads.File))
            DownloadQueue.DeleteDownloads(files)
        End Sub
        Public Shared Sub Recycle(ByVal files As Generic.List(Of Entities.Downloads.File))
            DownloadQueue.RecycleDownloads(files)
        End Sub

        Friend Shared ReadOnly Property PartSize() As Integer
            Get
                Return 2 * 1048576 ' 1024 * 1024, 1 Mb
            End Get
        End Property

        Friend Shared Function CalculatePartCountEx(ByVal fileSize As Long) As Integer
            Dim iRet As Integer = CInt(fileSize \ PartSize)
            If fileSize Mod PartSize <> 0 Then iRet += 1
            Return iRet
        End Function
        Friend Shared Function CreatePartStatusArray(ByVal fileSize As Long, ByVal defaultValue As Entities.Downloads.PartStatus) As Entities.Downloads.PartStatus()
            Dim iSize As Integer = CalculatePartCountEx(fileSize)
            Dim bRet(iSize - 1) As Entities.Downloads.PartStatus
            For i As Integer = 0 To bRet.Count - 1
                bRet(i) = defaultValue
            Next
            Return bRet
        End Function

        Public Shared Function GetCurrentReadRatesByFileId() As Generic.Dictionary(Of Integer, Integer)
            Dim dRet As New Generic.Dictionary(Of Integer, Integer)
            Dim aRatesByRefId As Generic.Dictionary(Of String, Integer) = Framework.Net.ThrottledNetworkStream.GetCurrentRatesByTrackingId

            For Each clsAP As ActivePart In DownloadQueue.GetAllActiveParts
                If aRatesByRefId.ContainsKey(clsAP.TrackingId) Then
                    If dRet.ContainsKey(clsAP.FileId) Then
                        dRet(clsAP.FileId) += aRatesByRefId(clsAP.TrackingId)
                    Else
                        dRet.Add(clsAP.FileId, aRatesByRefId(clsAP.TrackingId))
                    End If
                End If
            Next
            Return dRet
        End Function

    End Class



End Namespace

